package Thread;

import org.apache.commons.lang3.RandomUtils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;


import java.util.Iterator;
import java.util.concurrent.*;


/**
 * 阿里巴巴java规范说创建线程的注意事项：
 * 线程池不允许使用 Executors 去创建，而是通过 ThreadPoolExecutor 的方式，这样
 * 的处理方式让写的同学更加明确线程池的运行规则，规避资源耗尽的风险。
 * 说明：Executors 返回的线程池对象的弊端如下：
 * 1）FixedThreadPool 和 SingleThreadPool:
 * 允许的请求队列长度为 Integer.MAX_VALUE，可能会堆积大量的请求，从而导致 OOM。
 * 2）CachedThreadPool 和 ScheduledThreadPool:
 * 允许的创建线程数量为 Integer.MAX_VALUE，可能会创建大量的线程，从而导致 OOM。
 */

public class ThreadPoolExecutor_study {

    protected Logger logger = LoggerFactory.getLogger(getClass());


    /**
     * Spring Context 的线程池
     */
    @Test
    public void myTaskAsyncPool() {
        ThreadPoolTaskExecutor executor = null;
        try {
            executor = new ThreadPoolTaskExecutor();
            //核心线程数量，线程池创建时候初始化的线程数
            executor.setCorePoolSize(10);
            //最大线程数，只有在缓冲队列满了之后才会申请超过核心线程数的线程
            executor.setMaxPoolSize(20);
            //缓冲队列，用来缓冲执行任务的队列
            executor.setQueueCapacity(100);
            //当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
            executor.setKeepAliveSeconds(60);
            //设置好了之后可以方便我们定位处理任务所在的线程池
            executor.setThreadNamePrefix("QZD-BASE-SERVICE-ThreadPoolTaskExecutor");
            //用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
            executor.setWaitForTasksToCompleteOnShutdown(true);
            //该方法用来设置线程池中任务的等待时间，如果超过这个时候还没有销毁就强制销毁，以确保应用最后能够被关闭，而不是阻塞住。
            executor.setAwaitTerminationSeconds(60);
            // rejection-policy：当pool已经达到max size的时候，如何处理新任务
            // CALLER_RUNS：不在新线程中执行任务，而是由调用者所在的线程来执行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.initialize();
        } catch (Exception e) {
            logger.error("myTaskAsyncPool异常:{}", e);
        }


    }


    /**
     * 使用Executors创建线程方式：
     * <p>
     * 1、newSingleThreadExecutor
     * 创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。
     * 此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
     * new ThreadPoolExecutor(1, 1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
     * <p>
     * 2、newFixedThreadPool
     * 创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。
     * 线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。
     * new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
     * <p>
     * 3、newCachedThreadPool
     * 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，
     * 那么就会回收部分空闲（60秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。
     * 此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小。
     * new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue());
     * <p>
     * 4.newScheduledThreadPool(创建一个定长线程池，支持定时及周期性任务执行。延迟执行示例代码)
     * 实现类是： ScheduledThreadPoolExecutor，
     * 延迟队列是：DelayedWorkQueue
     */
    @Test
    public void createThreadByExecutors() {


        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();


        singleThreadExecutor.submit(() -> System.out.println("aaa"));


        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(1);
    }


    /**
     * ThreadPoolExecutor 线程池的的参数详解：
     * 用下面的参数创建一个新的线程池
     * 使用给的初始化和默认的线程工厂以及注入的执行器来参数创建一个ThreadPoolExecutor
     *
     * @param corePoolSize    核心线程池中保存的线程数量, 即使是在限制情况下也保持存在, 除非allowCoreThreadTimeOut被设置
     * @param maximumPoolSize 在线程池中创建的最大线程数量
     * @param keepAliveTime   当线程的数量大于核心线程时, 多于核心线程的线程数量在任务空闲时存在的最大时间
     * @param unit            keepAliveTime 参数的时间单位
     * @param workQueue       在任务执行前保存任务的队列，这些任务将被execute 方法提交给Runnable（被提交但是未被执行的任务）
     * @param threadFactory   生成线程池中线程的线程工厂
     * @param handler         当执行被阻塞的时候这个handler处理器被使用，因为达到了线程边界和队列容量：
     *                        RejectedExecutionHandler ：
     *                        AbortPolicy：直接抛出异常。
     *                        CallerRunsPolicy:会调用当前线程池的所在的线程去执行被拒绝的任务。
     *                        DiscardPolicy:会让被线程池拒绝的任务直接抛弃，不会抛异常也不会执行。
     *                        DiscardOldestPolicy:会抛弃任务队列中最旧的任务也就是最先加入队列的，再把这个新任务添加进去。
     * @throws IllegalArgumentException 如果下列任意一项成立:将抛出非法参数异常：
     *                                  corePoolSize < 0
     *                                  keepAliveTime <0
     *                                  maximumPoolSize<0
     *                                  maximumPoolSize < corePoolSize
     * @throws NullPointerException     如果  workQueue 为 null，将抛出空指针异常
     */

    public void ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException();
    }

    @Test
    public void testThreadPooling() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));

        executor.execute(new ThreadRunable());
        executor.execute(new ThreadRunable());
        executor.execute(new ThreadRunable());
        executor.execute(new ThreadRunable());


    }

    @Test
    public void testAbortPolicy() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 0, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<Runnable>(2), new ThreadPoolExecutor.AbortPolicy());

        for (int i = 1; i < 7; i++) {
//            System.out.println("添加第"+i+"个任务");
            executor.execute(new MyThread("线程" + i));
            Iterator iterator = executor.getQueue().iterator();
            while (iterator.hasNext()) {
                MyThread thread = (MyThread) iterator.next();
                System.out.println("列表：" + thread.name);
            }
        }
    }


    class ThreadRunable implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":" + RandomUtils.nextLong(1, 100));
        }
    }


    static class MyThread implements Runnable {
        String name;

        public MyThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程:" + Thread.currentThread().getName() + " 执行:" + name + "  run");
        }
    }
}
